from django.template.loader import render_to_string
from google.appengine.api import urlfetch
from bvuserapp.user import freebase_add_link_edit

import urllib
import cookielib
import random
import datetime
import sys
import os
import json_custom
import logging
from django.conf import settings

#LOGIN_REQUIRED_PREFIXES = getattr(settings, 'LOGIN_REQUIRED_PREFIXES', ())
#MEDIA_VERSION = unicode(settings.MEDIA_VERSION)

def get_domain_used():
    """ README!! - here we define what freebase we are reading/writing from/to!
        Returns the freebase domain used.  Currently this is either:
            freebase.com
            sandbox-freebase.com
             """
    return settings.FREEBASE_DOMAIN # set in settings.py

def get_bioventurist_domain():
    """ Returns the freebase bioventurist domain used.  
            For example: base/bioventurist/"""
    return settings.BVR_DOMAIN

def get_delimiter():
    """ A standard delimiter used in the BVR Code
        This method is depricated and should not be used.
    """
    return settings.BVR_DELIMITER


def login(domain, verbose):
    """ Returns the cookie string required for writing to freebase"""
    
    loginurl = 'http://' + domain + '/api/account/login?username=' + settings.BVR_FREEBASE_USER +'&password='+settings.BVR_FREEBASE_USER_PW+'&rememberme=true'

   # result = urlfetch.fetch(url='http://' + domain + '/api/account/login?username=bioventurist&password=123456&rememberme=true', #?queries='+ urllib.quote(html),
    result = urlfetch.fetch(url=loginurl, #?queries='+ urllib.quote(html),
                                payload={},
                                method=urlfetch.POST,
                                headers={
                                   'X-Metaweb-Request':'adsfadf',
                                })
    if (verbose) :
        logging.info('login set-cookies:\n' + result.headers['set-cookie'] + '\n')
    return result.headers['set-cookie']


def perform_upload(contents, guid, verbose, request=None):
    """ contents : file uploaded as part of request body 
        guid : type id/guid of an existing document to which this content object is linked to. This can be null and if so will be created for you

        To get the blurb upload working:
        metaweb.py : preform upload
        then use create_article.mql and link_article.mql
    """
    logging.info('perform_upload : ' + contents +'\n')
    logging.info('guid : ' + guid +'\n')

    if (False) :
        content_length = len(contents);
        logging.info(content_length)
        if (content_length > 30) :
            content_length = 30
        logging.info('mqlupload query:\n' + contents[0, content_length] + '\n')

    if (guid == None) :
        form_fields = {
                   "file":contents,
                   "_abc": random.random(),
                   "content_type" : "text/plain"
        }
    else :
        form_fields = {
                   "file":contents,
                   "_abc": random.random(),
                   "document": guid,
                   "content_type" : "text/plain"
        }
    cookies = login(get_domain_used(), verbose)
    logging.info('form_field:\n' + repr(form_fields) + '\n')

    form_data = urllib.urlencode(form_fields)
    result = urlfetch.fetch(
                            url='http://' + get_domain_used() + '/api/service/upload',
                            #payload=form_data,
                            payload=contents,
                            method=urlfetch.POST,
                            headers={
                                     #'Content-Type': 'application/x-www-form-urlencoded',
                                     'Content-Length': len(contents),
                                     'Content-Type': 'text/plain',
                                     'X-Metaweb-Request':'adsfadf',
                                     'Cookie' : cookies 
                                     }
                            )
    #log the result of the upload
    if (verbose) :
        logging.info('mqlwrite response 1 :\n' + result.content + '\n')
    if request != None:
        user = request.user
        user.store_edit(result)
    else: 
        logging.info('\nNo request object detected in upload\n')
    if (verbose) :
        logging.info('mqlwrite response:\n' + result.content + '\n')
    return result.content;


def perform_read(mql_file, options, verbose, request=None):
    """ Performs a freebase read 
            mql_file is the file containing the query
            options contain mql variables required by the mql_file
            verbose==true allows writing the query and results to the log
    """
    logging.info('mqlread query:\n');
    html = render_to_string(mql_file, options)
    if (verbose) :
		logging.info('mqlread query:\n' + html + '\n')
    html = html.replace("\n", "")
    form_fields = {
                  "queries":html,
                  "_abc": random.random()
                  }
    cookies = login(get_domain_used(), verbose)
    form_data = urllib.urlencode(form_fields)
    result = urlfetch.fetch(
	                        url='http://' + get_domain_used() + '/api/service/mqlread', #?queries='+ urllib.quote(html),
	                        payload=form_data,
	                        method=urlfetch.POST,
	                        headers={
	                                 'Content-Type': 'application/x-www-form-urlencoded',
	                                 'X-Metaweb-Request':'adsfadf',
	                                 'Cookie' : cookies 
	                                }
	                       )
    if (verbose) :
		logging.info('mqlread response:\n' + result.content + '\n')
    return result.content;
    
# performs a write given a mql file and an options map to fill in that file
def perform_write(mql_file, options, type, verbose, request=None):
    """ Performs a freebase write 
            mql_file is the file containing the query
            options contain mql variables required by the mql_file
            verbose==true allows writing the query and results to the log
        
        If a session is detected (non-null request passed in) then the user associated with this write is logged
        If the write originates from an http request, check that there is an associated authenticated user object
            If not, do not perform the write and respond with an error
            If yes, log the edit
        Log the result of the write to the database
    """

    logging.info('Performing write')
    
    allowedToWrite = False
    if request != None:
        user = request.user
        if  user.is_authenticated():
            allowedToWrite = True
        else:
            html = "<p>User failed authentication, please contact administration</p>"
            return html
    else: 
        allowedToWrite = True
        if(verbose):
            logging.info('\nNo request object detected\n')
    
    if (allowedToWrite):
        html = render_to_string(mql_file, options)
        html = html.replace('&quot;','"')
        if (verbose) :
            logging.info('mqlwrite query:\n' + html + '\n')
            html = html.replace("\n", "")
            form_fields = {
                   "queries":html,
                   "_abc": random.random()
                  }
            cookies = login(get_domain_used(), verbose)
            form_data = urllib.urlencode(form_fields)
            result = urlfetch.fetch(
                            url='http://' + get_domain_used() + '/api/service/mqlwrite', #?queries='+ urllib.quote(html),
                            payload=form_data,
                            method=urlfetch.POST,
                            headers={
                                     'Content-Type': 'application/x-www-form-urlencoded',
                                     'X-Metaweb-Request':'adsfadf',
                                     'Cookie' : cookies 
                                     }
                            )
            if (verbose) : 
                logging.info('mqlwrite response (in perform_write):\n' + result.content + '\n')
            if  (request!=None):
                user.store_edit(result, type)
    return result.content;

# performs a write given a mql file and an options map to fill in that file
def perform_write_full(write_mql_file, write_options, read_mql_file, read_options, type, verbose, request=None):
    """ Performs a freebase write 
            write_mql_file is the file containing the query
            write_options contain mql variables required by the write_mql_file
            verbose==true allows writing the query and results to the log
        
        If a session is detected (non-null request passed in) then the user associated with this write is logged
        If the write originates from an http request, check that there is an associated authenticated user object
            If not, do not perform the write and respond with an error
            If yes, log the edit
        Log the result of the write to the database
    """

    logging.info('Performing write_full')
    
    allowedToWrite = False
    if request != None:
        user = request.user
        if  user.is_authenticated():
            allowedToWrite = True
        else:
            html = "<p>User failed authentication, please contact administration</p>"
            return html
    else: 
        allowedToWrite = True
        if(verbose):
            logging.info('\nNo request object detected\n')
    
    if (allowedToWrite):
        html = render_to_string(write_mql_file, write_options)
        html = html.replace('&quot;','"')
        if (verbose) :
            logging.info('mqlwrite query:\n' + html + '\n')
            html = html.replace("\n", "")
            form_fields = {
                   "queries":html,
                   "_abc": random.random()
                  }
            cookies = login(get_domain_used(), verbose)
            form_data = urllib.urlencode(form_fields)
            result = urlfetch.fetch(
                            url='http://' + get_domain_used() + '/api/service/mqlwrite', #?queries='+ urllib.quote(html),
                            payload=form_data,
                            method=urlfetch.POST,
                            headers={
                                     'Content-Type': 'application/x-www-form-urlencoded',
                                     'X-Metaweb-Request':'adsfadf',
                                     'Cookie' : cookies 
                                     }
                            )
            if (verbose) : 
                logging.info('mqlwrite response (in perform_write_full):\n' + result.content + '\n')
            if  (request!=None):
                
                # Obtain extra info about the topics from the freebase database:
                html_read = perform_read(read_mql_file, read_options, verbose, request=None)
                if (verbose) : 
                    logging.info('mql read response (in perform_write_full):\n' + html_read + '\n')
                result_read = json.read(html_read)
                if (verbose) : 
                    logging.info(repr(result_read)+'\n')
                result_read = result_read["q1"]["result"]
                if (verbose) : 
                    logging.info('result :' + repr(result_read)+'\n')

                # store the edit
                user.store_edit_full(result, result_read, read_options, type, verbose)
    return result.content;


def create_topic(topic_type, topic_name, create_condition, request=None):
    """ Creates a new topic in Freebase
            topic_type is the type(s) of the topic
            topic_name is the name of the topic
            create_condition defines whether:   
                        the topic should be created unless it already exists (create_condition = 'unless_exists')
                        the topic should be created even if the same article name & type exists (create_condition = "unconditional') 
    """
    logging.info('create_conditon: ' + repr(create_condition)+'\n')
    html = perform_write(
         'mql/create_topic.mql',
         {
            'create_condition' : create_condition,
            'topic_type': topic_type, 
            'topic_name': topic_name, 
         },
         True,
         request
         )
    logging.info(html);
    return html

def create_topic_unconditional(topic_type, topic_name, request=None):
    """ Creates a new topic in Freebase even if the topic name & type exist
            topic_type is the type(s) of the topic
            topic_name is the name of the topic
    """
    html = create_topic(topic_type, topic_name, "unconditional", request)
    return html


def link_topic(topic1_id, topic1_property, topic2_id, request=None):
    """ Links two topics together in Freebase
            topic1_id is the guid of the first topic
            topic1_property is the property in topic1 that links topic2
            topic2_id is the guid of the second topic
    """
    # Write to freebase
    html = perform_write_full(
         'mql/link_topics_through_property_min.mql',
         {
            'topic1_id': topic1_id, 
            'topic1_property': topic1_property, 
            'topic2_id': topic2_id,
         },
        'mql/link_topics_read.mql',          
        {
            'topic1_id': topic1_id, 
            'topic1_property': topic1_property, 
            'topic2_id': topic2_id,
         },
         'link',
         True, 
         request
         )
    
    # Read topic info from freebase
    
    #########################################
    # Record the data in the datastore:
    #########################################
    obj = json.read(html);
    logging.info(repr(obj)+'\n');
    result = obj["q1"]["result"];
    logging.info('result :' + repr(result)+'\n');
    
    # Test if the insertion worked:
    connect = result[topic1_property]["connect"]
    logging.info('connect :' + repr(connect)+'\n');
    #if(connect == "inserted") :
        #freebase_add_link_edit(topic1_id, topic1_property, topic2_add_id)
    
    return html

def process_write_multi(request):
    """ Writes to Freebase an array of topics (topic2s) to another topic (topic1)
            request = data is passed as a http POST.  The request should contain 
            json strings (to be turned into objects) for the required topic1 data 
            and the topic2 data that is required for the linking.  For example:
            
            topic1 =     {'property': '/base/bioventurist/science_or_technology_company/develops_treatments_for_disease', 
                        'id': '/en/ulthera_inc'}
            
            topic2 =     {'type_for_create': '["/base/bioventurist/bv_medical_condition"]', 
                        'topics_to_add': 
                            {'data': [{
                                'id': '/en/ovarian_cancer', 
                                'val': 'Ovarian cancer', 
                                'create_new': 'false'
                                }]
                            }, 
                        'type': ['/base/bioventurist/bv_medical_condition'], 
                        'topics_to_remove': 
                            {'data': [{
                                'id': '#9202a8c04000641f80000000000821ec', 
                                'val': 'Breast cancer'
                                }]
                            }
                        }

    """
    
    # obtain the values to add and to remove
    topic1 = json.read(request.POST.__getitem__('topic1').encode('utf-8'))
    logging.info('topic1 : ' + repr(topic1) + '\n')
    topic1_id = topic1['id']
    topic1_property = topic1['property']
    
    topic2 = json.read(request.POST.__getitem__('topic2').encode('utf-8'))
    logging.info('topic2 : ' + repr(topic2) + '\n')
    topic2_to_add = topic2['topics_to_add']
    topic2_to_remove = topic2['topics_to_remove']    
    topic2_type = topic2['type']
    topic2_type_for_create = topic2['type_for_create']

    # output to the log for debugging
    logging.info('\n');
    logging.info('topic1_id : ' + topic1_id + '\n')
    logging.info('topic1_property : ' + topic1_property + '\n')
    logging.info('topic2_to_add : ' + repr(topic2_to_add) + '\n')
    logging.info('topic2_to_remove : ' + repr(topic2_to_remove) + '\n')
    logging.info('topic2_type : ' + repr(topic2_type) + '\n')
    logging.info('topic2_type_for_create : ' + repr(topic2_type_for_create) + '\n')

    # link all the add values
    add_index = 0;
    topic2_to_add = topic2_to_add['data'];
    for topic2_add in topic2_to_add:
        # first see if the topic needs to be created:
        topic2_add_id = topic2_add['id'];
        if topic2_add['create_new'] == 'true':
            topic2_name = topic2_add['val'];
            html_create = create_topic(topic2_type_for_create, topic2_name, "unless_exists", request);

            # get the newly created id
            obj = json.read(html_create);
            result = obj["q1"]["result"];
            topic2_add_id = result["id"];
            logging.info('topic2_add_id :' + repr(topic2_add_id)+'\n');
        
        if topic2_add_id != '':
            html = link_topic(topic1_id, topic1_property, topic2_add_id, request)
        
        add_index = add_index + 1;

    # unlink all the remove values        
    topic2_to_remove = topic2_to_remove['data'];                   
    for topic2_remove in topic2_to_remove:
        if topic2_remove['id'] != '':
            html = perform_write(
                     'mql/unlink_topics_through_property_min.mql',
                     {
                        'topic1_id': topic1_id, 
                        'topic1_property': topic1_property, 
                        'topic2_id': topic2_remove['id'],
                     },
                     True,
                     request
                     )
    return html        

def process_write_mixed(request):
    """ Writes to Freebase:
    
    vals_to_add : 
        [{
            'type': 'link'            
            'topic1': 
                {
                'property': '/base/bioventurist/bv_therapeutic/indication_or_disease', 
                'guid': '#9202a8c04000641f8000000008ceb17c'
                }, 
            'topic2': 
                [{
                    'name': '', 
                    'create_new': 'true', 
                    'guid': '', 
                    'type': ['indication'], 
                    'id': ''
                    'elements_to_add': [{
                        'topic1': 
                            {
                            'property': '/base/bioventurist/bv_therapeutic/indication_or_disease', 
                            'guid': ''
                            }, 
                        'topic2': 
                            [{
                                'name': '/en/cancer', 
                                'elements_to_add': [], 
                                'create_new': 'false', 
                                'guid': '/en/cancer', 
                                'type': ['/base/bioventurist/bv_medical_condition'], 
                                'id': ''
                            }], 
                        'type': 'link'
                        }], 
                }], 
        }]
    """
    # obtain the values to add and to remove
    vals_to_add = json.read(request.POST.__getitem__('vals_to_add').encode('utf-8'))
    logging.info('vals_to_add : ' + repr(vals_to_add) + '\n')
    vals_to_remove = json.read(request.POST.__getitem__('vals_to_remove').encode('utf-8'))
    logging.info('vals_to_remove : ' + repr(vals_to_remove) + '\n')
    
    # first remove any values
    for val_to_remove in vals_to_remove:
        write_mixed_remove_vals(val_to_remove['topic1'], val_to_remove['topic2'], request)

    # add values
    for val_to_add in vals_to_add:
        write_mixed_add_vals(val_to_add['topic1'], val_to_add['topic2'], request)

def write_mixed_remove_vals(topic1, topic2, request):
    logging.info('write_mixed_remove_vals\n');
    logging.info('topic1 : '+repr(topic1)+'\n');
    logging.info('topic2 : '+repr(topic2)+'\n');

    # add the value
    html = perform_write(
             'mql/unlink_topics_through_property_min.mql',
             {
                'topic1_id': topic1['guid'], 
                'topic1_property': topic2['topic1_property'], 
                'topic2_id': topic2['guid'],
             },
             True,
             request
             )
    
    
def write_mixed_add_vals(topic1, topic2s, request):
    logging.info('write_mixed_add_vals\n');
    logging.info('topic1 : '+repr(topic1)+'\n');
    logging.info('topic2s : '+repr(topic2s)+'\n');
            
    # add of the topic2s to topic1
    for topic2 in topic2s:
        logging.info('topic2 : '+repr(topic2)+'\n');

        # first see if topic2 needs to be added new:
        if(topic2['create_new']=='true'):
            if (topic2['force_create'] == 'true'):
                html_create = create_topic_unconditional(topic2['type'], topic2['name'], request)
            else:
                html_create = create_topic(topic2['type'], topic2['name'], "unless_exists", request)

            # get the newly created id
            obj = json.read(html_create)
            result = obj["q1"]["result"]
            topic2_add_id = result["id"]
            logging.info('topic2_add_id :' + repr(topic2_add_id)+'\n')
            topic2['guid']=topic2_add_id
            
            # for all topic2's elements to add, set the topic1_id 
            for topic2_element_to_add in topic2['elements_to_add']:
                topic2_element_to_add['topic1']['guid'] = topic2_add_id;
            
            
        if is_primative(topic2['type']):
            #full_property_path = parent_property + '/' + topic1_property_name;
            html = perform_write(
                     'mql/update_primative.mql',
                     {
                        'property_name': topic2['topic1_property'], 
                        'value': topic2['name'], 
                        'topic_id': topic1['guid'],
                     },
                     True,
                     request
                     )
        else: #if it is not a primitive, link topics    
            # add the value
            html = perform_write(
                     'mql/link_topics_through_property_min.mql',
                     {
                        'topic1_id': topic1['guid'], 
                        'topic1_property': topic2['topic1_property'], 
                        'topic2_id': topic2['guid'],
                     },
                     True,
                     request
                     )
        
        # add any values to topic2 if required
        for element_to_add in topic2['elements_to_add']:
            write_mixed_add_vals(element_to_add['topic1'], element_to_add['topic2'], request)
            

def is_primative(type):
    if type == '/type/text':
        return True
    if type == '/type/uri':
        return True
    if type == '/type/float':
        return True

def process_write_compound(request, topicName):
    logging.debug('process_write_compound : ' + topicName + '\n')
    
    # Obtain all the values to be written
    parent_topic_id = request.POST.__getitem__('parent_topic_id')
    parent_property = request.POST.__getitem__('parent_property')
    
    topic1 = json.read(request.POST.__getitem__('topic1_vals').encode('utf-8'))
    topic2 = json.read(request.POST.__getitem__('topic2_vals').encode('utf-8'))

    logging.debug('parent_topic_id : ' + repr(parent_topic_id) + '\n')
    logging.debug('parent_property : ' + repr(parent_property) + '\n')
    logging.debug('topic1 : ' + repr(topic1) + '\n')
    logging.debug('topic2 : ' + repr(topic2) + '\n')
        
    # Write each new value:
    # HERE!!!!
    topic1_vals = topic1['data']
    topic1_type = topic1['type']
    topic2_vals = topic2['data']
    
    logging.debug('topic1_vals : ' + repr(topic1_vals) + '\n')
    logging.debug('topic1_type: ' + repr(topic1_type) + '\n')
    logging.debug('topic2_vals : ' + repr(topic2_vals) + '\n')
    index = 0;
    for topic2 in topic2_vals:
        logging.debug('topic1_val : ' + repr(topic1_vals[index]) + '\n')
        topic1_val = topic1_vals[index]
        topic1_id = topic1_val['id']
        
        topic2_val = topic2['val'];
        topic2_id = topic2['id'];
        topic1_property_type = topic1_val['property_type'];
        topic1_property_name = topic1_val['property_name'];
        topic1_property_name_full = topic1_type + '/' + topic1_property_name;
        topic2_row_index = topic2['row_index']
        
        # First see if the topic1_id exists (eg. that the compound topic exits).
        if (topic1_id == 'null' or topic1_id == 'undefined'):
            # if the topic does not exist, we need to create it:
            html_create = create_topic_unconditional('"'+topic1_type+'"', '', request);
            obj = json.read(html_create);
            logging.debug(repr(obj)+'\n');
            result = obj["q1"]["result"];
            logging.debug('result :' + repr(result)+'\n');
            topic1_id = result["id"];
            logging.debug('topic1_id :' + repr(topic1_id)+'\n');

            link_topic(parent_topic_id, parent_property, topic1_id, request)
            
            # set the topic1_id for any properties in the same compound element
            #j=0;
            #for row_number in topic2_row_indexes:
            #    if topic2_row_index == row_number:
            #        topic1_ids[j] = topic1_id;
            #        j = j + 1;
                    
        
        # first see if the property is a primitive:
        logging.debug('topic1_property_type :' + topic1_property_type+'\n');
        if topic1_property_type == '/type/text' or topic1_property_type == '/type/uri' or topic1_property_type == '/type/float':
            full_property_path = parent_property + '/' + topic1_property_name;
            html = perform_write(
                     'mql/update_primative.mql',
                     {
                        'property_name': topic1_property_name, 
                        'value': topic2_val, 
                        'topic_id': topic1_id,
                        'topic_type': topic1_type,
                     },
                     True,
                     request
                     )
        else: #if it is not a primitive, link topics
            
            # first see if the topic needs to be created
            
            if topic2['create_new'] == 'true':
                topic2_name = topic2_val;
                topic2_create_type = topic2['create_type'];
                html_create = create_topic(topic2_create_type, topic2_name, 'unless_exists', request);
                #html_create = perform_write(
                #         'mql/create_topic.mql',
                #         {
                #            'topic_type': topic2_create_type, 
                #            'topic_name': topic2_name, 
                #         },
                #         True
                #         )
                # Get the new topic2_id from the created page
                obj = json.read(html_create);
                logging.debug(repr(obj)+'\n');
                result = obj["q1"]["result"];
                logging.debug('result :' + repr(result)+'\n');
                topic2_id = result["id"];
                logging.debug('topic2_id :' + repr(topic2_id)+'\n');

            html = link_topic(topic1_id, topic1_property_name_full, topic2_id, request)
            #html = perform_write(
            #         'mql/link_topics_through_property_min.mql',
            #         {
            #            'topic1_id': topic1_id, 
            #            'topic1_property': topic1_property_name_full, 
            #            'topic2_id': topic2_id,
            #         },
            #         True
            #         )
        index=index+1;
    return html

def get_bvr_type(type_list):
    '''
        Convert a list of freebase types to a single BVR_type
    '''
    for type in type_list:
        if type == '/business/company':
            return 'company'
        elif type == '/base/bioventurist/science_or_technology_company':
            return 'company'
        elif type == '/base/bioventurist/technology_class':
            return 'technology'
        elif type == '/medicine/disease':
            return 'disease'
        elif type == '/base/bioventurist/bv_medical_condition':
            return 'disease'

    return 'n/a'
